/*
 Copyright (c) 2023 Broin tianjianchen chengchaochao wangyingming All rights reserved.
 Use of this source code is governed by a BSD-style
 license that can be found in the LICENSE file.
 */

// This must be first
#include "vmlinux.h"

// These must be below vmlinux.h
#include <bpf/bpf_core_read.h> /* for BPF CO-RE helpers */
#include <bpf/bpf_helpers.h>   /* most used helpers: SEC, __always_inline, etc */
#include <bpf/bpf_tracing.h>   /* for getting kprobe arguments */
#include <linux/errno.h>

#include "../include/kernel_def.h"
#include "../../include/struct.h"
#include "../include/maps.h"
#include "../include/map_defs.h"
#include "../include/audit.h"
#include "../include/policy.h"

#define EPERM 1
#define AF_INET 2

#define PATH_MAX 4096
char LICENSE[] SEC("license") = "Dual BSD/GPL";

unsigned long host_mntns = 0;
unsigned long container_id = 0;

/**
 * @description:
 * @param {struct linux_binpsrm *} bprm
 * @return {int} 捕获结果，0表示结束
 */

// 创建文件的hook
SEC("lsm/path_mknod")
int BPF_PROG(path_mknod, struct path *dir, struct dentry *dentry, umode_t mode, unsigned int dev)
{
    int ret = 0;

    // 获取父目录的inode
    struct inode *dir_inode;
    dir_inode = dir->dentry->d_inode;
    u64 inode_id = BPF_CORE_READ(dir_inode, i_ino);
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    u64 mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);
    struct qstr d_name;
    // 读取 dentry 的名字
    bpf_probe_read(&d_name, sizeof(d_name), &dir->dentry->d_name);
    bpf_printk("current name is %s", d_name.name);
    // 获取主体
    subject_t sub = {};
    bpf_get_current_comm(&sub.comm, sizeof(sub.comm));
    u32 *id = bpf_map_lookup_elem(&containers_mntns, &mntns);
    ret = lsm_inode_perm(&sub, dir_inode, FILE_MAY_APPEND);

    return ret;
}

// 删除文件的hook
SEC("lsm/path_unlink")
int BPF_PROG(path_unlink, struct path *dir, struct dentry *dentry)
{
    int ret = 0;

    // 获取父目录的inode
    struct inode *dir_inode;
    dir_inode = dir->dentry->d_inode;

    // 获取主体
    subject_t sub = {};
    bpf_get_current_comm(&sub.comm, sizeof(sub.comm));
    ret = lsm_inode_perm(&sub, dir_inode, FILE_MAY_DELETE);
    return ret;
}

// 创建文件夹的hook
SEC("lsm/path_mkdir")
int BPF_PROG(path_mkdir, struct path *dir, struct dentry *dentry, umode_t mode)
{
    int ret = 0;

    // 获取父目录的inode
    struct inode *dir_inode;
    dir_inode = dir->dentry->d_inode;

    // 获取主体
    subject_t sub = {};
    bpf_get_current_comm(&sub.comm, sizeof(sub.comm));
    ret = lsm_inode_perm(&sub, dir_inode, FILE_MAY_APPEND);
    return ret;
}

// 删除文件夹的hook
SEC("lsm/path_rmdir")
int BPF_PROG(path_rmdir, struct path *dir, struct dentry *dentry)
{
    int ret = 0;

    // 获取父目录的inode
    struct inode *dir_inode;
    dir_inode = dir->dentry->d_inode;

    // 获取主体
    subject_t sub = {};
    bpf_get_current_comm(&sub.comm, sizeof(sub.comm));
    ret = lsm_inode_perm(&sub, dir_inode, FILE_MAY_DELETE);
    return ret;
}

SEC("lsm/path_rename")
int BPF_PROG(path_rename, struct path *old_dir, struct dentry *old_dentry, struct path *new_dir, struct dentry *new_dentry)
{
    int ret = 0;
    struct mv_old_inode *event;
    // 获取主体
    subject_t sub = {};
    bpf_get_current_comm(&sub.comm, sizeof(sub.comm));

    // 获取旧父目录是否具有写的权限
    struct inode *olddir_inode;
    olddir_inode = old_dir->dentry->d_inode;
    ret = lsm_inode_perm(&sub, olddir_inode, FILE_MAY_WRITE);
    if (ret != 0)
        return ret;

    // 获取新父目录是否具有写的权限
    struct inode *newdir_inode;
    newdir_inode = new_dir->dentry->d_inode;
    ret = lsm_inode_perm(&sub, newdir_inode, FILE_MAY_WRITE);
    if (ret != 0)
        return ret;
    // 传递给用户态的信息
    char new_path[64];
    struct qstr d_name;
    // 读取 dentry 的名字
    bpf_probe_read(&d_name, sizeof(d_name), &new_dentry->d_name);
    // 读取名字字符串
    // bpf_probe_read(new_path, sizeof(new_path), d_name.name);
    // bpf_printk("d_name is %s",d_name.name);
    struct inode *old_inode = old_dentry->d_inode;
    u64 old_inode_key = old_inode->i_ino;
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    // event = bpf_ringbuf_reserve(&old_pid_inode,sizeof(*event), 0);
    // if (!event) {
    // 	return 0;
    // }
    // event->pid = pid;
    // event->inode_id = old_inode_key;
    // bpf_probe_read(event->new_path, sizeof(event->new_path), d_name.name);
    // //提交到ringbuf中
    // bpf_ringbuf_submit(event,  0);

    return 0;
}

SEC("tp/sched/sched_process_exec")
int handle_exec(struct trace_event_raw_sched_process_exec *ctx)
{
    // TODO:在进程执行后，首先查看是否为主机进程
    u64 gid = bpf_get_current_pid_tgid();
    u64 pid = bpf_get_current_pid_tgid() >> 32;
    u64 mntns;
    char comm[70];
    bpf_get_current_comm(&comm, sizeof(comm));
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    mntns = BPF_CORE_READ(task, nsproxy, mnt_ns, ns.inum);
    if (!host_mntns)
    {
        host_mntns = mntns;
    }

    if (mntns == host_mntns)
    {
        return 0;
    }

    if (bpf_map_lookup_elem(&containers_host, &mntns))
    {
        return 0;
    }

    u32 *con_id = bpf_map_lookup_elem(&containers_mntns, &mntns);
    if (!con_id)
    {
        container_id += 1;
        bpf_map_update_elem(&containers_mntns, &mntns, &container_id, BPF_ANY);
        struct container_event *event;
        event = bpf_ringbuf_reserve(&container_path, sizeof(*event), 0);
        if (!event)
        {
            return 0;
        }

        event->pid = pid;
        bpf_ringbuf_submit(event, 0);
    }
    else
    {
    }
    return 0;
}

SEC("lsm/file_permission")
int BPF_PROG(file_permission, struct file *file, int mask)
{
    int ret = 0;

    subject_t sub = {};
    sub.type = PROC;
    bpf_get_current_comm(&sub.comm, sizeof(sub.comm));

    ret = lsm_inode_perm(&sub, file->f_inode, mask_to_access(file->f_inode, mask));
    return ret;
}

SEC("lsm/socket_connect")
int BPF_PROG(socket_connect, struct socket *sock, struct sockaddr *address, int addrlen)
{
    int ret = 0;

    subject_t sub = {};
    u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;

    sub.type = USER;
    sub.uid = uid;

    struct sockaddr_in *addr = (struct sockaddr_in *)address;

    ret = lsm_net_perm(&sub, addr, NETWORK_MAY_SOCKET_CONNECT);

    return ret;
}